<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
<script>MathJax = {tex: {inlineMath: [["\\(", "\\)"]]}, svg: {fontCache: "global"}};</script>
<script type="text/javascript" id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/default.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
<script src="../static/mccole.js"></script>
<script>window.onload = () => fixPage()</script>

    <script defer data-domain="stjs.tech" src="https://plausible.io/js/plausible.js"></script>

    <link href="../static/mccole.css" rel="stylesheet" type="text/css">
    <title>Software Design by Example: <h1 id="debugger">Debugger</h1></title>
  </head>
  <body>
    <main>
      <p class="home"><a href="../">Software Design by Example</a></p>
      <h1 id="debugger">Debugger</h1>
      <div class="lede"><p>Running programs under the control of a breakpointing debugger</p></div>
      <nav>
	<ol class="toc">
<li><a href="#debugger-start">What is our starting point?</a></li>
<li><a href="#debugger-tracing">How can we make a tracing debugger?</a></li>
<li><a href="#debugger-interactive">How can we make the debugger interactive?</a></li>
<li><a href="#debugger-test">How can we test an interactive application?</a></li>
<li><a href="#debugger-exercises">Exercises</a></li>
</ol>

      </nav>
      <div class="keyterms"><p><a href="../glossary/#breakpoint">breakpoint</a>, <a href="../glossary/#source_map">source map</a>, <a href="../glossary/#tab_completion">tab completion</a>, <a href="../glossary/#watchpoint">watchpoint</a></p></div>

      
<p>We have finally come to one of the topics that sparked this book:
how does a <span i="debugger">debugger</span> work?
(The other was layout engines, discussed in <a class="secref" href="../layout-engine/#layout-engine">Chapter&nbsp;11</a>.)
Debuggers are as much a part of good programmers' lives as version control
but are taught far less often
(in part, we believe, because it's harder to create homework questions for them).
In this chapter we will build a simple single-stepping debugger;
in doing so,
we will show one way to test interactive applications (<a class="secref" href="../unit-test/#unit-test">Chapter&nbsp;4</a>).</p>
<h2 id="debugger-start">What is our starting point?</h2>
<p>We would like to debug a higher-level language than the <span i="assembly code">assembly code</span> of <a class="secref" href="../virtual-machine/#virtual-machine">Chapter&nbsp;19</a>,
but we don't want to have to write a parser
or wrestle with the <span i="abstract syntax tree">ASTs</span> of <a class="secref" href="../style-checker/#style-checker">Chapter&nbsp;14</a>.
As a compromise,
we will represent programs as JSON data structures
whose element have the form <code>[command ...args]</code>:</p>
<pre title="filter-base.json"><code class="language-json">// const a = [-3, -5, -1, 0, -2, 1, 3, 1]
// const b = Array()
// let largest = a[0]
// let i = 0
// while (i &lt; length(a)) {
//   if (a[i] &gt; largest) {
//     b.push(a[i])
//   }
//   i += 1
// }
// i = 0
// while (i &lt; length(b)) {
//   console.log(b[i])
//   i += 1
// }

[
  [&quot;defA&quot;, &quot;a&quot;, [&quot;data&quot;, -3, -5, -1, 0, -2, 1, 3, 1]],
  [&quot;defA&quot;, &quot;b&quot;, [&quot;data&quot;]],
  [&quot;defV&quot;, &quot;largest&quot;, [&quot;getA&quot;, &quot;a&quot;, [&quot;num&quot;, 0]]],
  [&quot;append&quot;, &quot;b&quot;, [&quot;getV&quot;, &quot;largest&quot;]],
  [&quot;defV&quot;, &quot;i&quot;, [&quot;num&quot;, 0]],
  [&quot;loop&quot;, [&quot;lt&quot;, [&quot;getV&quot;, &quot;i&quot;], [&quot;len&quot;, &quot;a&quot;]],
    [&quot;test&quot;, [&quot;gt&quot;, [&quot;getA&quot;, &quot;a&quot;, [&quot;getV&quot;, &quot;i&quot;]], [&quot;getV&quot;, &quot;largest&quot;]],
      [&quot;setV&quot;, &quot;largest&quot;, [&quot;getA&quot;, &quot;a&quot;, [&quot;getV&quot;, &quot;i&quot;]]],
      [&quot;append&quot;, &quot;b&quot;, [&quot;getV&quot;, &quot;largest&quot;]]
    ],
    [&quot;setV&quot;, &quot;i&quot;, [&quot;add&quot;, [&quot;getV&quot;, &quot;i&quot;], [&quot;num&quot;, 1]]]
  ],
  [&quot;setV&quot;, &quot;i&quot;, [&quot;num&quot;, 0]],
  [&quot;loop&quot;, [&quot;lt&quot;, [&quot;getV&quot;, &quot;i&quot;], [&quot;len&quot;, &quot;b&quot;]],
    [&quot;print&quot;, [&quot;getA&quot;, &quot;b&quot;, [&quot;getV&quot;, &quot;i&quot;]]],
    [&quot;setV&quot;, &quot;i&quot;, [&quot;add&quot;, [&quot;getV&quot;, &quot;i&quot;], [&quot;num&quot;, 1]]]
  ]
]
</code></pre>
<p>Our <span i="virtual machine">virtual machine</span> is structured like the one in <a class="secref" href="../virtual-machine/#virtual-machine">Chapter&nbsp;19</a>.
A real system would parse a program to create JSON,
then translate JSON into assembly code,
then assemble that to create machine instructions.
Again,
to keep things simple we will execute a program by
removing comments and blank lines
and then running commands by looking up the command name's and calling that method:</p>
<pre title="vm-base.js"><code class="language-js">import assert from 'assert'

class VirtualMachineBase {
  constructor (program) {
    this.program = this.compile(program)
    this.prefix = '&gt;&gt;'
  }

  compile (lines) {
    const text = lines
      .map(line =&gt; line.trim())
      .filter(line =&gt; (line.length &gt; 0) &amp;&amp; !line.startsWith('//'))
      .join('\n')
    return JSON.parse(text)
  }

  run () {
    this.env = {}
    this.runAll(this.program)
  }

  runAll (commands) {
    commands.forEach(command =&gt; this.exec(command))
  }

  exec (command) {
    const [op, ...args] = command
    assert(op in this,
      `Unknown op &quot;${op}&quot;`)
    return this[op](args)
  }

}

export default VirtualMachineBase
</code></pre>
<!-- continue -->
<p>Remember, functions and methods are just another kind of data,
so if an object has a method called <code>&quot;meth&quot;</code>,
the expression <code>this[&quot;meth&quot;]</code> looks it up
and <code>this[&quot;meth&quot;](args)</code> calls it.
If <code>&quot;meth&quot;</code> is stored in a variable called <code>name</code>,
then <code>this[name](args)</code> will do exactly the same thing.</p>
<p>The method in our VM that defines a new variable with an initial value looks like this:</p>
<pre title="vm-base.js"><code class="language-js">  defV (args) {
    this.checkOp('defV', 2, args)
    const [name, value] = args
    this.env[name] = this.exec(value)
  }
</code></pre>
<!-- continue -->
<p>while the one that adds two values looks like this:</p>
<pre title="vm-base.js"><code class="language-js">  add (args) {
    this.checkOp('add', 2, args)
    const left = this.exec(args[0])
    const right = this.exec(args[1])
    return left + right
  }
</code></pre>
<p>Running a <code>while</code> loop is:</p>
<pre title="vm-base.js"><code class="language-js">  loop (args) {
    this.checkBody('loop', 1, args)
    const body = args.slice(1)
    while (this.exec(args[0])) {
      this.runAll(body)
    }
  }
</code></pre>
<!-- continue -->
<p>and checking that a variable name refers to an array is:</p>
<pre title="vm-base.js"><code class="language-js">  checkArray (op, name) {
    this.checkName(op, name)
    const array = this.env[name]
    assert(Array.isArray(array),
      `Variable &quot;${name}&quot; used in &quot;${op}&quot; is not array`)
  }
</code></pre>
<p>The other operations are similar to these.</p>
<h2 id="debugger-tracing">How can we make a tracing debugger?</h2>
<p>The next thing we need in our debugger is
a <span g="source_map" i="source map; debugger!source map">source map</span> that keeps track of
where in the source file each instruction came from.
Since JSON is a subset of JavaScript,
we could get line numbers by parsing our programs with <span i="Acorn"><a href="https://github.com/acornjs/acorn">Acorn</a></span>.
However,
we would then have to scrape the information we want for this example out of the AST.
Since this chapter is supposed to be about debugging,
not parsing,
we will instead cheat and add a line number to each interesting statement by hand
so that our program looks like this:</p>
<pre title="filter-source-map.json"><code class="language-json">[
  [1, &quot;defA&quot;, &quot;a&quot;, [&quot;data&quot;, -3, -5, -1, 0, -2, 1, 3, 1]],
  [2, &quot;defA&quot;, &quot;b&quot;, [&quot;data&quot;]],
  [3, &quot;defV&quot;, &quot;largest&quot;, [&quot;getA&quot;, &quot;a&quot;, [&quot;num&quot;, 0]]],
  [4, &quot;append&quot;, &quot;b&quot;, [&quot;getV&quot;, &quot;largest&quot;]],
  [5, &quot;defV&quot;, &quot;i&quot;, [&quot;num&quot;, 0]],
  [6, &quot;loop&quot;, [&quot;lt&quot;, [&quot;getV&quot;, &quot;i&quot;], [&quot;len&quot;, &quot;a&quot;]],
   [7, &quot;test&quot;, [&quot;gt&quot;, [&quot;getA&quot;, &quot;a&quot;, [&quot;getV&quot;, &quot;i&quot;]], [&quot;getV&quot;, &quot;largest&quot;]],
    [8, &quot;setV&quot;, &quot;largest&quot;, [&quot;getA&quot;, &quot;a&quot;, [&quot;getV&quot;, &quot;i&quot;]]],
    [9, &quot;append&quot;, &quot;b&quot;, [&quot;getV&quot;, &quot;largest&quot;]]
   ],
   [11, &quot;setV&quot;, &quot;i&quot;, [&quot;add&quot;, [&quot;getV&quot;, &quot;i&quot;], [&quot;num&quot;, 1]]]
  ],
  [13, &quot;setV&quot;, &quot;i&quot;, [&quot;num&quot;, 0]],
  [14, &quot;loop&quot;, [&quot;lt&quot;, [&quot;getV&quot;, &quot;i&quot;], [&quot;len&quot;, &quot;b&quot;]],
   [15, &quot;print&quot;, [&quot;getA&quot;, &quot;b&quot;, [&quot;getV&quot;, &quot;i&quot;]]],
   [16, &quot;setV&quot;, &quot;i&quot;, [&quot;add&quot;, [&quot;getV&quot;, &quot;i&quot;], [&quot;num&quot;, 1]]]
  ]
]
</code></pre>
<p>Building the source map from that is simple;
for now,
we just modify <code>exec</code> to ignore the line number:</p>
<pre title="vm-source-map.js"><code class="language-js">import assert from 'assert'

import VirtualMachineBase from './vm-base.js'

class VirtualMachineSourceMap extends VirtualMachineBase {
  compile (lines) {
    const original = super.compile(lines)
    this.sourceMap = {}
    const result = original.map(command =&gt; this.transform(command))
    return result
  }

  transform (node) {
    if (!Array.isArray(node)) {
      return node
    }
    if (Array.length === 0) {
      return []
    }
    const [first, ...rest] = node
    if (typeof first !== 'number') {
      return [first, null, ...rest.map(arg =&gt; this.transform(arg))]
    }
    const [op, ...args] = rest
    this.sourceMap[first] =
      [op, first, ...args.map(arg =&gt; this.transform(arg))]
    return this.sourceMap[first]
  }

  exec (command) {
    const [op, lineNum, ...args] = command
    assert(op in this,
      `Unknown op &quot;${op}&quot;`)
    return this[op](args)
  }
}

export default VirtualMachineSourceMap
</code></pre>
<blockquote>
<h3>It's not really cheating</h3>
<p>We said that adding line numbers by hand was cheating,
but it isn't.
What we're actually doing is deferring a problem until we're sure we need to solve it.
If our approach is clumsy or fails outright because of some aspect of design we didn't foresee,
there will have been no point handling line numbers the &quot;right&quot; way.
A good rule for <span i="software design!deferring problems">software design</span>
is to tackle the thing you're least sure about first,
using temporary code in place of what you think you'll eventually need.</p>
</blockquote>
<p>The next step is to modify the VM's <code>exec</code> method
so that it executes a callback function for each significant operation
(where &quot;significant&quot; means &quot;we bothered to record its line number&quot;).
Since we're not sure what our debugger is going to need,
we give this callback the environment holding the current set of variables,
the line number,
and the operation being performed:</p>
<pre title="vm-callback.js"><code class="language-js">import assert from 'assert'

import VirtualMachineSourceMap from './vm-source-map.js'

class VirtualMachineCallback extends VirtualMachineSourceMap {
  constructor (program, dbg) {
    super(program)
    this.dbg = dbg
    this.dbg.setVM(this)
  }

  exec (command) {
    const [op, lineNum, ...args] = command
    this.dbg.handle(this.env, lineNum, op)
    assert(op in this,
      `Unknown op &quot;${op}&quot;`)
    return this[op](args, lineNum)
  }

  message (prefix, val) {
    this.dbg.message(`${prefix} ${val}`)
  }
}

export default VirtualMachineCallback
</code></pre>
<p>We also modify the VM's constructor to record the debugger and give it a reference to the virtual machine
(<a class="figref" href="#debugger-initialization">Figure&nbsp;20.1</a>).
We have to <span i="mutual references">connect the two objects explicitly</span>
because each one needs a reference to the other,
but one of them has to be created first.
&quot;A gets B then B tells A about itself&quot; is a common pattern;
we will look at other ways to manage it in the exercises.</p>
<figure id="debugger-initialization">
  <img src="figures/initialization.svg" alt="Initializing mutually-depending objects" />
  <figcaption>Figure&nbsp;20.1: Two-step initialization of mutually-dependent objects.</figcaption>
</figure>
<p>To run the program,
we create a debugger object and pass it to the VM's constructor:</p>
<pre title="run-debugger.js"><code class="language-js">import assert from 'assert'

import readSource from './read-source.js'

const main = () =&gt; {
  assert(process.argv.length === 5,
    'Usage: run-debugger.js ./vm ./debugger input|-')
  const VM = require(process.argv[2])
  const Debugger = require(process.argv[3])
  const inFile = process.argv[4]
  const lines = readSource(inFile)
  const dbg = new Debugger()
  const vm = new VM(lines, dbg)
  vm.run()
}

main()
</code></pre>
<p>A simple debugger just traces interesting statements as they run:</p>
<pre title="debugger-trace.js"><code class="language-js">import DebuggerBase from './debugger-base.js'

class DebuggerTrace extends DebuggerBase {
  handle (env, lineNum, op) {
    if (lineNum !== null) {
      console.log(`${lineNum} / ${op}: ${JSON.stringify(env)}`)
    }
  }
}

export default DebuggerTrace
</code></pre>
<p>Let's try it on a program that adds the numbers in an array:</p>
<pre title="sum-source-map.json"><code class="language-json">// const a = [-5, 1, 3]
// const total = 0
// let i = 0
// while (i &lt; length(a)) {
//   total += a[i]
//   i += 1
// }
// console.log(total)

[
  [1, &quot;defA&quot;, &quot;a&quot;, [&quot;data&quot;, -5, 1, 3]],
  [2, &quot;defV&quot;, &quot;total&quot;, [&quot;num&quot;, 0]],
  [3, &quot;defV&quot;, &quot;i&quot;, [&quot;num&quot;, 0]],
  [4, &quot;loop&quot;, [&quot;lt&quot;, [&quot;getV&quot;, &quot;i&quot;], [&quot;len&quot;, &quot;a&quot;]],
    [5, &quot;setV&quot;, &quot;total&quot;,
      [&quot;add&quot;, [&quot;getV&quot;, &quot;total&quot;], [&quot;getA&quot;, &quot;a&quot;, [&quot;getV&quot;, &quot;i&quot;]]]
    ],
    [8, &quot;setV&quot;, &quot;i&quot;, [&quot;add&quot;, [&quot;getV&quot;, &quot;i&quot;], [&quot;num&quot;, 1]]]
  ],
  [10, &quot;print&quot;, [&quot;getV&quot;, &quot;total&quot;]]
]
</code></pre>
<h2 id="debugger-interactive">How can we make the debugger interactive?</h2>
<p>What we have built so far is an always-on <code>print</code> statement.
To turn it into an interactive debugger,
we will use the <a href="https://www.npmjs.com/package/prompt-sync"><code>prompt-sync</code></a> module to manage user input
with the following set of commands:</p>
<ul>
<li>
<p><code>?</code> or <code>help</code> to list commands.</p>
</li>
<li>
<p><code>clear #</code> to clear a <span g="breakpoint">breakpoint</span> at a numbered line.</p>
</li>
<li>
<p><code>list</code> to list lines and breakpoints.</p>
</li>
<li>
<p><code>next</code> to go forward one line.</p>
</li>
<li>
<p><code>print name</code> to show a variable while at a breakpoint.</p>
</li>
<li>
<p><code>run</code> to run to the next breakpoint.</p>
</li>
<li>
<p><code>stop #</code> to break at a numbered line.</p>
</li>
<li>
<p><code>variables</code> to list all variable names.</p>
</li>
<li>
<p><code>exit</code> to exit immediately.</p>
</li>
</ul>
<p>When the virtual machine calls the debugger,
the debugger first checks whether or not it is on a numbered line.
If it isn't,
it hands control back to the VM.
Otherwise,
its action depends on our current state.
If we are single-stepping or if this line is a breakpoint,
Otherwise, it does nothing.</p>
<p>The overall structure of the interactive debugger is:</p>
<pre title="debugger-interactive.js"><code class="language-js">import prompt from 'prompt-sync'

import DebuggerBase from './debugger-base.js'

const PROMPT_OPTIONS = { sigint: true }

class DebuggerInteractive extends DebuggerBase {
  constructor () {
    super()
    this.singleStep = true
    this.breakpoints = new Set()
    this.lookup = {
      '?': 'help',
      c: 'clear',
      l: 'list',
      n: 'next',
      p: 'print',
      r: 'run',
      s: 'stop',
      v: 'variables',
      x: 'exit'
    }
  }

  handle (env, lineNum, op) {
    if (lineNum === null) {
      return
    }
    if (this.singleStep) {
      this.singleStep = false
      this.interact(env, lineNum, op)
    } else if (this.breakpoints.has(lineNum)) {
      this.interact(env, lineNum, op)
    }
  }

}

export default DebuggerInteractive
</code></pre>
<!-- continue -->
<p>It interacts with users by lookup up a command and invoking the corresponding method,
just as the VM does:</p>
<pre title="debugger-interactive.js"><code class="language-js">  interact (env, lineNum, op) {
    let interacting = true
    while (interacting) {
      const command = this.getCommand(env, lineNum, op)
      if (command.length === 0) {
        continue
      }
      const [cmd, ...args] = command
      if (cmd in this) {
        interacting = this[cmd](env, lineNum, op, args)
      } else if (cmd in this.lookup) {
        interacting = this[this.lookup[cmd]](env, lineNum, op, args)
      } else {
        this.message(`unknown command ${command} (use '?' for help)`)
      }
    }
  }

  getCommand (env, lineNum, op) {
    const options = Object.keys(this.lookup).sort().join('')
    const display = `[${lineNum} ${options}] `
    return this.input(display)
      .split(/\s+/)
      .map(s =&gt; s.trim())
      .filter(s =&gt; s.length &gt; 0)
  }

  input (display) {
    return prompt(PROMPT_OPTIONS)(display)
  }
</code></pre>
<blockquote>
<h3>Learning as we go</h3>
<p>We didn't originally put the input and output in methods that could be overridden,
but realized later we needed to do this to make the debugger testable.
Rather than coming back and rewriting this,
we have done it here.</p>
</blockquote>
<p>With this structure in place,
the command handlers are pretty straightforward.
For example,
this method moves us to the next line:</p>
<pre title="debugger-interactive.js"><code class="language-js">  next (env, lineNum, op, args) {
    this.singleStep = true
    return false
  }
</code></pre>
<!-- continue -->
<p>while this one prints the value of a variable:</p>
<pre title="debugger-interactive.js"><code class="language-js">  print (env, lineNum, op, args) {
    if (args.length !== 1) {
      this.message('p[rint] requires one variable name')
    } else if (!(args[0] in env)) {
      this.message(`unknown variable name &quot;${args[0]}&quot;`)
    } else {
      this.message(JSON.stringify(env[args[0]]))
    }
    return true
  }
</code></pre>
<p>After using this for a few moments,
though
we realized that we needed to change the signature of the <code>loop</code> method.
We want to stop the loop each time it runs,
and need to know where we are.
We didn't allow for this in the base class,
and we don't want to have to change every method,
so we take advantage of the fact that JavaScript ignores any extra arguments passed to a method:</p>
<pre title="vm-interactive.js"><code class="language-js">import VirtualMachineCallback from './vm-callback.js'

class VirtualMachineInteractive extends VirtualMachineCallback {
  loop (args, lineNum) {
    this.checkBody('loop', 1, args)
    const body = args.slice(1)
    while (this.exec(args[0])) {
      this.dbg.handle(this.env, lineNum, 'loop')
      this.runAll(body)
    }
  }
}

export default VirtualMachineInteractive
</code></pre>
<!-- continue -->
<p>This is sloppy, but it works;
we will tidy it up in the exercises.</p>
<h2 id="debugger-test">How can we test an interactive application?</h2>
<p>How can we <span i="unit test!interactive application">test</span> an interactive application like a debugger?
The answer is, &quot;By making it non-interactive.&quot;
Like many tools over the past thirty years,
our approach is based on a program called <span i="Expect"><a href="https://en.wikipedia.org/wiki/Expect">Expect</a></span>.
Our library replaces the input and output functions of the application being tested with callbacks,
then provides input when asked and checks output when it is given
(<a class="figref" href="#debugger-test-interact">Figure&nbsp;20.2</a>).</p>
<figure id="debugger-test-interact">
  <img src="figures/test-interact.svg" alt="Testing interactive application" />
  <figcaption>Figure&nbsp;20.2: Replacing input and output to test interactive applications.</figcaption>
</figure>
<!-- continue -->
<p>The results look like this:</p>
<pre title="test/test-expect.js"><code class="language-js">describe('interactive debugger', () =&gt; {
  it('runs and prints', (done) =&gt; {
    setup('print-0.json')
      .get('[1 ?clnprsvx] ')
      .send('r')
      .get('&gt;&gt; 0')
      .run()
    done()
  })

  it('breaks and resumes', (done) =&gt; {
    setup('print-3.json')
      .get('[1 ?clnprsvx] ')
      .send('s 3')
      .get('[1 ?clnprsvx] ')
      .send('r')
      .get('&gt;&gt; 0')
      .get('&gt;&gt; 1')
      .get('[3 ?clnprsvx] ')
      .send('x')
      .run()
    done()
  })
})
</code></pre>
<p>Our <code>Expect</code> class may be short,
but it is hard to understand because it is so abstract:</p>
<pre title="expect.js"><code class="language-js">import assert from 'assert'

class Expect {
  constructor (subject, start) {
    this.start = start
    this.steps = []
    subject.setTester(this)
  }

  send (text) {
    this.steps.push({ op: 'toSystem', arg: text })
    return this
  }

  get (text) {
    this.steps.push({ op: 'fromSystem', arg: text })
    return this
  }

  run () {
    this.start()
    assert.strictEqual(this.steps.length, 0,
      'Extra steps at end of test')
  }

  toSystem () {
    return this.next('toSystem')
  }

  fromSystem (actual) {
    const expected = this.next('fromSystem')
    assert.strictEqual(expected, actual,
      `Expected &quot;${expected}&quot; got &quot;${actual}&quot;`)
  }

  next (kind) {
    assert(this.steps.length &gt; 0,
      'Unexpected end of steps')
    assert.strictEqual(this.steps[0].op, kind,
      `Expected ${kind}, got &quot;${this.steps[0].op}&quot;`)
    const text = this.steps[0].arg
    this.steps = this.steps.slice(1)
    return text
  }
}

export default Expect
</code></pre>
<!-- continue -->
<p>Piece by piece:</p>
<ul>
<li><code>subject</code> is the thing being tested.</li>
<li><code>start</code> is a callback to start the system running.
It gives control to the subject,
which then calls back into the test framework for input and output.</li>
<li><code>get</code> and <code>send</code> store things to be given to the subject
and to be checked against its output.
Both methods return <code>this</code> so that we can chain calls together.</li>
<li><code>run</code> starts the system
and checks that all expected interactions have been used up when testing is done.</li>
<li><code>toSystem</code> and <code>fromSystem</code> use <code>next</code> to get the next test record,
check its type,
and return the string.</li>
</ul>
<p>Let's modify the debugger to use the tester,
keeping in mind that the prompt counts as an output
(and yes, we forgot this in the first version):</p>
<pre title="debugger-test.js"><code class="language-js">import DebuggerInteractive from './debugger-interactive.js'

class DebuggerTest extends DebuggerInteractive {
  constructor () {
    super()
    this.tester = null
  }

  setTester (tester) {
    this.tester = tester
  }

  input (display) {
    this.tester.fromSystem(display)
    return this.tester.toSystem()
  }

  message (m) {
    this.tester.fromSystem(m)
  }
}

export default DebuggerTest
</code></pre>
<p>Again,
we can't pass the tester as a constructor parameter because of initialization order,
so we write a <code>setup</code> function to make sure everything is connected the right way:</p>
<pre title="test/test-expect.js"><code class="language-js">import Expect from '../expect.js'
import VM from '../vm-interactive.js'
import Debugger from '../debugger-test.js'
import readSource from '../read-source.js'

const setup = (filename) =&gt; {
  const lines = readSource(path.join('debugger/test', filename))
  const dbg = new Debugger()
  const vm = new VM(lines, dbg)
  return new Expect(dbg, () =&gt; vm.run())
}
</code></pre>
<p>Let's try running our tests:</p>
<pre title="test-expect.sh"><code class="language-sh">npm run test -- -g 'interactive debugger'
</code></pre>


<pre title="test-expect.out"><code class="language-out">
&gt; stjs@1.0.0 test /u/stjs
&gt; mocha */test/test-*.js &quot;-g&quot; &quot;interactive debugger&quot;



  interactive debugger
    ✓ runs and prints
</code></pre>
<p>That works---or does it?
Why is only one test shown,
and doesn't the summary appear?
After a bit of digging,
we realize that the debugger's <code>exit</code> command calls <code>process.exit</code> when the simulated program ends,
so the whole program including the VM, debugger, and test framework stops immediately
<em>before</em> the promises that contain the tests have run.</p>
<p>We could fix this by modifying the debugger callback
to return an indication of whether or not execution should continue,
then modify the VM to pay attention to that flag.
However,
this approach becomes very complicated when we have deeply-nested calls to <code>exec</code>,
which will happen with loops and conditionals.</p>
<p>A better alternative is to use an <span i="exception!for control flow">exception for control flow</span>.
We can define our own kind of exception as an empty class:
it doesn't need any data
because we are only using it to get a typed object:</p>
<pre title="halt-exception.js"><code class="language-js">class HaltException {
}

export default HaltException
</code></pre>
<!-- continue -->
<p>Next,
we modify the debugger to throw this exception when asked to exit:</p>
<pre title="debugger-exit.js"><code class="language-js">import HaltException from './halt-exception.js'
import DebuggerTest from './debugger-test.js'

class DebuggerExit extends DebuggerTest {
  exit (env, lineNum, op, args) {
    throw new HaltException()
  }
}

export default DebuggerExit
</code></pre>
<!-- continue -->
<p>And finally
we modify the VM to finish cleanly if this exception is thrown,
but re-throw any other kind of exception:</p>
<pre title="vm-exit.js"><code class="language-js">import HaltException from './halt-exception.js'
import VirtualMachineInteractive from './vm-interactive.js'

class VirtualMachineExit extends VirtualMachineInteractive {
  run () {
    this.env = {}
    try {
      this.runAll(this.program)
    } catch (exc) {
      if (exc instanceof HaltException) {
        return
      }
      throw exc
    }
  }
}

export default VirtualMachineExit
</code></pre>
<p>With these changes in place,
we are finally able to test our interactive debugger:</p>
<pre title="test-exit.sh"><code class="language-sh">npm run test -- -g 'exitable debugger'
</code></pre>


<pre title="test-exit.out"><code class="language-out">
&gt; stjs@1.0.0 test /u/stjs
&gt; mocha */test/test-*.js &quot;-g&quot; &quot;exitable debugger&quot;



  exitable debugger
    ✓ runs and prints
    ✓ breaks and resumes


  2 passing (7ms)
</code></pre>
<h2 id="debugger-exercises">Exercises</h2>
<h3 class="exercise">Implementing tab completion</h3>
<p>Read the documentation for <a href="https://www.npmjs.com/package/prompt-sync"><code>prompt-sync</code></a>
and then implement <span g="tab_completion">tab completion</span>
for the debugger.</p>
<h3 class="exercise">Modifying variables while running</h3>
<p>Add a <code>set</code> command that sets the value of a variable to a new value in a running program.
How do you handle setting array elements?</p>
<h3 class="exercise">Making output more readable</h3>
<p>Modify the tracing debugger so that
the statements inside loops and conditionals are indented for easier reading.</p>
<h3 class="exercise">Better loops</h3>
<p>Our solution for handling loops is sloppy; fix it.</p>
<h3 class="exercise">Using a flag to continue execution</h3>
<p>Modify the debugger and virtual machine to use a &quot;continue executing&quot; flag
rather than throwing an exception when execution should end.
Which approach is easier to understand?
Which will be easier to extend in future?</p>
<h3 class="exercise">Numbering lines</h3>
<p>Write a tool that takes a JSON program representation <em>without</em> statement numbers
and produces one that numbers all of the interesting statements for debugging purposes.
Use whatever definition of &quot;interesting&quot; you think would be most useful.</p>
<h3 class="exercise">Looping around again</h3>
<p>Implement a &quot;next loop iteration&quot; command that runs the program
until it reaches the current point in the next iteration of the current loop.</p>
<h3 class="exercise">Looking up objects</h3>
<p>Rather than having some objects call <code>setXYZ</code> methods in other objects,
it is common practice to use a lookup table for mutual dependencies:</p>
<ol>
<li>
<p>Every object initializes calls <code>table.set(name, this)</code> in its constructor.</p>
</li>
<li>
<p>Whenever object A needs the instance of object B,
it calls <code>table.lookup('B')</code>.
It does <em>not</em> store the result in a member variable.</p>
</li>
</ol>
<p>Modify the virtual machine and debugger to use this pattern.</p>
<h3 class="exercise">Watching for variable changes</h3>
<p>Modify the debugger and virtual machine to implement <span g="watchpoint">watchpoints</span>
that halt the program whenever the value of a variable changes.</p>
<h3 class="exercise">Translating JSON to assembler</h3>
<p>Write a tool that translates the JSON program representation
into the assembly code of <a class="secref" href="../virtual-machine/#virtual-machine">Chapter&nbsp;19</a>.
To simplify things,
increase the number of registers so that
there is always storage for intermediate results
when doing arithmetic.</p>

    </main>
    <footer>
  <hr/>
  <p>
    Copyright © 2022 Greg Wilson
    &nbsp;
    <a href="../license/"><img class="icon" src="../static/cc-by-nc.svg" alt="CC-BY-NC icon"/></a>
    <a href="../license/"><img class="icon" src="../static/hippocratic.svg" alt="Hippocratic License icon"/></a>
    <a href="https://github.com/software-tools-books/stjs/"><img class="icon" src="../static/github.svg" alt="GitHub icon"/></a>
    <a href="mailto:gvwilson@third-bit.com"><img class="icon" src="../static/email.svg" alt="email icon"/></a>
  </p>
</footer>

  </body>
</html>
